package com.zx.auth.block.repository.custom.impl;

import com.zx.auth.block.modle.OAuth2RegisteredClient;
import com.zx.auth.block.param.OAuth2ClientParam;
import com.zx.auth.block.repository.custom.OAuth2RegisteredClientCustomRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * description: OAuth2注册客户端自定义仓库实现 <br>
 * create: 2025-11-13 10:05
 * </p>
 *
 * @author
 */
@Repository
public class OAuth2RegisteredClientCustomRepositoryImpl implements OAuth2RegisteredClientCustomRepository {

    // 注入EntityManager实例，用于执行JPA查询
    private final EntityManager entityManager;

    @Autowired
    public OAuth2RegisteredClientCustomRepositoryImpl(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    /**
     * 根据条件分页查询客户端
     *
     * @param clientParam 查询条件
     * @param pageable    分页对象
     * @param tenantId    租户ID
     * @return 客户端分页结果
     */
    @Override
    public Page<OAuth2RegisteredClient> findClientsByPage(OAuth2ClientParam clientParam, Pageable pageable, String tenantId) {
        // 获取CriteriaBuilder实例，用于构建查询
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        // 创建一个返回OAuth2RegisteredClient类型的CriteriaQuery
        CriteriaQuery<OAuth2RegisteredClient> criteriaQuery = cb.createQuery(OAuth2RegisteredClient.class);

        // 从OAuth2RegisteredClient实体类创建一个根节点，用于后续的查询构建
        Root<OAuth2RegisteredClient> clientRoot = criteriaQuery.from(OAuth2RegisteredClient.class);

        // 构建查询条件
        Predicate[] predicates = buildPredicates(clientRoot, clientParam, cb, tenantId);

        // 设置查询的WHERE子句，并添加默认排序
        criteriaQuery.where(predicates).orderBy(cb.desc(clientRoot.get("clientIdIssuedAt")));

        // 创建一个TypedQuery，用于执行具体的查询
        TypedQuery<OAuth2RegisteredClient> typedQuery = entityManager.createQuery(criteriaQuery);

        // 设置查询的起始位置和结果数量限制
        typedQuery.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
        typedQuery.setMaxResults(pageable.getPageSize());

        // 执行查询并获取结果列表
        List<OAuth2RegisteredClient> content = typedQuery.getResultList();

        // 计算总记录数
        Long total = countByClient(clientParam, tenantId);

        // 创建并返回PageImpl对象，封装分页结果
        return new PageImpl<>(content, pageable, total);
    }

    /**
     * 根据客户端ID和租户ID查找客户端
     *
     * @param clientId 客户端ID
     * @param tenantId 租户ID
     * @return 客户端列表
     */
    @Override
    public List<OAuth2RegisteredClient> findByClientIdAndTenantId(String clientId, String tenantId) {
        // 获取CriteriaBuilder实例，用于构建查询
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        // 创建一个返回OAuth2RegisteredClient类型的CriteriaQuery
        CriteriaQuery<OAuth2RegisteredClient> criteriaQuery = cb.createQuery(OAuth2RegisteredClient.class);

        // 从OAuth2RegisteredClient实体类创建一个根节点，用于后续的查询构建
        Root<OAuth2RegisteredClient> clientRoot = criteriaQuery.from(OAuth2RegisteredClient.class);

        // 构建查询条件
        List<Predicate> predicates = new ArrayList<>();
        predicates.add(cb.equal(clientRoot.get("clientId"), clientId));
        predicates.add(cb.equal(clientRoot.get("tenantId"), tenantId));

        // 设置查询的WHERE子句
        criteriaQuery.where(predicates.toArray(new Predicate[0]));

        // 创建一个TypedQuery，用于执行具体的查询
        TypedQuery<OAuth2RegisteredClient> typedQuery = entityManager.createQuery(criteriaQuery);

        // 执行查询并返回结果
        return typedQuery.getResultList();
    }

    /**
     * 根据条件查询客户端数量
     *
     * @param clientParam 查询条件
     * @param tenantId    租户ID
     * @return 记录总数
     */
    private Long countByClient(OAuth2ClientParam clientParam, String tenantId) {
        // 获取CriteriaBuilder实例，用于构建查询
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        // 创建一个返回Long类型的CriteriaQuery
        CriteriaQuery<Long> criteriaQuery = cb.createQuery(Long.class);

        // 从OAuth2RegisteredClient实体类创建一个根节点，用于后续的查询构建
        Root<OAuth2RegisteredClient> clientRoot = criteriaQuery.from(OAuth2RegisteredClient.class);

        // 构建查询条件
        Predicate[] predicates = buildPredicates(clientRoot, clientParam, cb, tenantId);

        // 设置查询的SELECT子句，统计满足条件的记录数
        criteriaQuery.select(cb.count(clientRoot)).where(predicates);

        // 创建一个TypedQuery，用于执行具体的查询
        TypedQuery<Long> typedQuery = entityManager.createQuery(criteriaQuery);

        // 执行查询并返回结果
        return typedQuery.getSingleResult();
    }

    /**
     * 构建查询条件
     *
     * @param clientRoot  根节点
     * @param clientParam 查询条件
     * @param cb          CriteriaBuilder实例
     * @param tenantId    租户ID
     * @return 查询条件数组
     */
    private Predicate[] buildPredicates(Root<OAuth2RegisteredClient> clientRoot, OAuth2ClientParam clientParam, CriteriaBuilder cb, String tenantId) {
        // 创建一个列表来存储查询条件
        List<Predicate> predicates = new ArrayList<>();

        // 如果查询条件的ID不为空且非空字符串，则添加一个等于条件
        if (clientParam.getId() != null && !clientParam.getId().trim().isEmpty()) {
            predicates.add(cb.equal(clientRoot.get("id"), clientParam.getId()));
        }

        // 如果查询条件的客户端ID不为空且非空字符串，则添加一个等于条件
        if (clientParam.getClientId() != null && !clientParam.getClientId().trim().isEmpty()) {
            predicates.add(cb.equal(clientRoot.get("clientId"), clientParam.getClientId()));
        }

        // 如果查询条件的客户端名称不为空且非空字符串，则添加一个模糊匹配条件
        if (clientParam.getClientName() != null && !clientParam.getClientName().trim().isEmpty()) {
            predicates.add(cb.like(clientRoot.get("clientName"), "%" + clientParam.getClientName() + "%"));
        }

        // 添加租户ID条件
        predicates.add(cb.equal(clientRoot.get("tenantId"), tenantId));

        // 将列表转换为数组并返回
        return predicates.toArray(new Predicate[0]);
    }
}